PostgreSQL 中 database、schema、table 和 view

在关系型数据库中,schema 表示数据对象的集合,这个集合可以包含各种对象如:表、视图、存储过程、索引等

简单来说,一个关系型数据库,从层级上来说可以分三级:database.schema.table。但并非所有的数据库实现都实现了 schema 这一级,在 MySQL 中几乎就把 schema 等同于了 database:

In MySQL, physically, a schema is synonymous with a database. You can substitute the keyword SCHEMA instead of DATABASE in MySQL SQL syntax, for example using CREATE SCHEMA instead of CREATE DATABASE.

但在 PostgreSQL 中,一个 database 可以包含多个 schema,一个 schema 又可以包含多个数据库对象:table、view、sequence、index、stored procedures、database links 这些等等

这样一个 database 中的不同 schema 中可以有重复名称的数据库对象、同一个 database 也可以通过多个 schema 分割出不同的权限、区隔

总的来说,使用 schema 有如下特点:

公共的schema

PostgreSQL 中有一个默认的 schema 是「public」,每一个 database 都会有这个 schema,当我们创建表单但未指定 schema 时,使用的就是 public 这个 schema

postgres=# CREATE TABLE products ( ... );

等价于下面这种写法:

postgres=# CREATE TABLE public.products ( ... );

创建schema

要创建一个新的 schema 使用 CREATE SCHEMA 指令:

postgres=# CREATE SCHEMA myschema;

要在 schema 中建立或存取表单使用「.」将两者串联起来:

postgres=# SELECT * from schema.table

实际上更一般的写法是:

postgres=# SELECT * from database.schema.table

也就是写全路径。不过并不必要,这只是语法意义上的表达,实际中,即使你写了数据库的名称,也得先连接到数据库。所以写不写数据库名称是不必要的

要移除一个 schema,必须先清空其中的数据库对象

postgres=# DROP SCHEMA myschema;

或者使用级联的方式一并删除:

postgres=# DROP SCHEMA myschema CASCADE;

schema的权限

通常,创建一个 schema 是为了某个使用者的,可以在创建时直接分配:

postgres=# CREATE SCHEMA schema_name AUTHORIZATION user_name;

当然,也可以将创建好的 schema 权限赋予新的用户

postgres=# GRANT USAGE ON schema_name TO user_name;

只有被赋予了 USAGE 权限的用户才能对 schema 进行存取操作。预设上,所有用户都被赋予了 public 这个 schema 的 USAGE 和 CREATE 权限

同样的,也可以移除权限:

postgres=# REVOKE CREATE ON SCHEMA public FROM user_name;

注意,可以撤销掉对 public 这个 schema 的访问许可,甚至把 public schema 直接移除也可以,这样每个用户就真正的限定在了他们自己的 schema 里

schema的搜索路径

户登录到系统,连接到一个数据库后,是通过该数据库的 search_path 来寻找 schema 的搜索顺序。可以通过命令 SHOW search_path 来查看当前搜索路径:

postgres=# SHOW search_path;

预设的情况是:

search_path
--------------
 "$user", public

可以看见,第一个搜索目标就是与当前用户同名的 schema,如果沒有同名的,将会继续搜索 public schema,我们可以通过 SET search_path 来更新当前 schema 路径(包括更新搜索顺序):

postgres=# SET search_path TO "$user", myschema, public;

官方建议是:在管理员创建一个具体数据库后,应该为所有可以连接到该数据库的用户分别创建一个与用户名相同的 schema,然后,将 search_path 设置为 $user,即默认的 schema 是与用户名相同的

当然,只要你有该 schema 的权限,永远可以用 schema.table 的方式访问到内容。search_path 只是表明了缺省条件下的搜寻 schema 的路径顺序

除了 public 以及使用者自行建立的 schema 之外,每一个 database 还有一个称作 pg_catalog 的 schema,它包含了系统资讯的资料表和内建的资料型别、函数、及运算子。 pg_catlog 永远都都是搜寻路径里的有效项目。它没有明确地显示在 search_path 里,但却是隐含优先搜寻,在那些明定的搜寻项目之前。这是为了确保内建的物件的名称都能被找到。然而,你可以把 pg_catlog 放在搜寻路径的最后面,如果你希望自订的同名 schema 能优先被使用的话

系统用的资料表都以「pg_」开头,为的是确保不会有冲突的情况出现,以免将来新的系统资料表和你现在所定义的资料表同名。(以预设的搜寻路径来说,一个简单的资料表使用,会直接被同名的系统资料表取代)。系统资料表会一直遵循这个命名规则,就不会产生冲突,只要使用者不使用「pg_」开头的命名方式就好了

另外,在标准 SQL 中并没有 public schema 的概念。如果要追求极致的相融标准的话,你就不应该使用,或移除 public schema

PostgreSQL 的 view

最后,再来说说 view(视图)。view 是一种虚拟表,它通常是用户有查询需要时,一种预定义的,查询出的结果的组合

PostgreSQL 视图是只读的,因此可能无法在视图上执行 DELETE、INSERT 或 UPDATE 语句。但是可以在视图上创建一个触发器,当尝试 DELETE、INSERT 或 UPDATE 视图时触发,需要做的动作在触发器内容中定义

创建一个 view:

postgres=# CREATE [TEMP | TEMPORARY] VIEW view_name AS
  SELECT column1, column2.....
  FROM table_name
  WHERE [condition];

可以在 SELECT 语句中包含多个表,这与在正常的 SQL SELECT 查询中的方式非常相似。如果使用了可选的 TEMP 或 TEMPORARY 关键字,则将在临时数据库中创建视图

一个实例:

postgres=# CREATE VIEW COMPANY_VIEW AS SELECT ID, NAME, AGE FROM COMPANY;

现在,可以查询 COMPANY_VIEW,与查询实际表的方式类似:

postgres=# SELECT * FROM COMPANY_VIEW;

删除视图,只需使用带有 view_name 的 DROP VIEW 语句

postgres=# DROP VIEW view_name;

参考

PostgreSQL 中文使用手册:5.9. Schemas
PostgreSQL 16 Documentation Chapter 5. Data Definition
PostgreSQL的模式、表、空间、用户间的关系
理解PostgreSQL数据库、模式、表、空间、用户间的关系
PostgreSQL里面的一些命令小结